﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using Microsoft.SharePoint.Utilities;

namespace Examples.CAMLFilter
{
    public class CAMLFilter : WebPart, ICAMLFilter
    {

        #region Параметры веб-части, которые редактируются через FilterEditor

        /// <summary>
        /// Показывать дебаг инфо или нет
        /// </summary>
        [WebBrowsable(false)]
        [Personalizable(PersonalizationScope.Shared)]
        public Boolean ShowDebug { get; set; }

        /// <summary>
        /// Показывать строчку о том, применялся фильтр или нет
        /// </summary>
        [WebBrowsable(false)]
        [Personalizable(PersonalizationScope.Shared)]
        public Boolean ShowInfo { get; set; }

        /// <summary>
        /// Объединять фильтры условием OR вместо AND
        /// </summary>
        [WebBrowsable(false)]
        [Personalizable(PersonalizationScope.Shared)]
        public Boolean UseOrConnections { get; set; }

        /// <summary>
        /// Список определенных фильтров
        /// </summary>
        [WebBrowsable(false)]
        [Personalizable(PersonalizationScope.Shared)]
        public List<Filter> Filters { get; set; }


        /// <summary>
        /// ID присоединенного списка. Сохраняем при настройке поля для передачи в форму поиска
        /// </summary>
        public Guid ListId { get; set; }

        #endregion

        /// <summary>
        /// Текущая локаль
        /// </summary>
        private Locale locale = new Locale();

        private void log(String str, params object[] data)
        {
            EnsureChildControls();

            for (int i = 0; i < data.Length; i++)
            {
                data[i] = SPEncode.HtmlEncodePreserveSpaces(data[i].ToString());
            }
            pMain.Controls.Add(new LiteralControl(String.Format(str, data) + "<br/>"));
        }

        Panel pMain;

        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            // Панелька для вывода разных всякостей
            if (pMain == null)
            {
                pMain = new Panel() { };

                // Стиль зависит от режима
                pMain.Attributes.Add("style", ((WebPartManager != null && WebPartManager.DisplayMode != WebPartManager.BrowseDisplayMode) || ShowDebug) ? "padding: 5px; background-color: #fefedd; border-style: solid; border-width: 1px; border-color: #c0c000;" : "padding-left: 10px; padding-top: 3px; padding-right: 3px; padding-bottom: 3px;");
            }
            this.Controls.Add(pMain);

        }


        protected override void OnPreRender(EventArgs e)
        {

            base.OnPreRender(e);

            EnsureChildControls();

            // Проверку на подключение делаем здесь, потому что если не подключены - SetConnectionInterface не срабатывает
            if (_provider == null || !(_provider is Microsoft.SharePoint.WebPartPages.XsltListViewWebPart))
                pMain.Controls.Add(new LiteralControl(String.Format(SPUtility.GetLocalizedString("$Resources:ErrorPrompt", "Examples.CAMLFilter", locale), SPUtility.GetLocalizedString("$Resources:ErrNotConnected", "Examples.CAMLFilter", locale)) + "<br/>"));

            // Если есть что показывать - показываем
            pMain.Visible = (pMain.Controls.Count > 0);

        }

        // Добавляем свой контрол для редактирования вебчастей
        public override EditorPartCollection CreateEditorParts()
        {
            ArrayList editorParts = new ArrayList();
            editorParts.Add(
                new FilterEditor()
                {
                    Title = SPUtility.GetLocalizedString("$Resources:WPFilterEditorTitle", "Examples.CAMLFilter", locale),
                    ID = this.ID + "_FilterEditor"
                }
            );
            return new EditorPartCollection(editorParts);
        }

        #region IWebPartTable consumer implementation

        // Отдаем для чтения наружу
        public Microsoft.SharePoint.WebPartPages.XsltListViewWebPart ListView
        {
            get { return _provider as Microsoft.SharePoint.WebPartPages.XsltListViewWebPart; }
        }

        private IWebPartTable _provider;

        [ConnectionConsumer("Columns for filter")]
        public void SetConnectionInterface(IWebPartTable provider)
        {
            EnsureChildControls();

            _provider = provider;

            // Вся работа по подмене схемы List View - здесь. В CreateChildControl уже поздно.
            try
            {
                // Убеждаемся, что подключены к правильной вебчасти
                if (_provider == null || !(_provider is Microsoft.SharePoint.WebPartPages.XsltListViewWebPart))
                {
                    _provider = null;
                    return;
                }

                Microsoft.SharePoint.WebPartPages.XsltListViewWebPart listView = _provider as Microsoft.SharePoint.WebPartPages.XsltListViewWebPart;

                if (WebPartManager.DisplayMode != WebPartManager.BrowseDisplayMode || ShowDebug) log(SPUtility.GetLocalizedString("$Resources:LogConnected", "Examples.CAMLFilter", locale), listView.DisplayTitle);
                if (WebPartManager.DisplayMode != WebPartManager.BrowseDisplayMode || ShowDebug) log(SPUtility.GetLocalizedString("$Resources:LogFilters", "Examples.CAMLFilter", locale), Filters == null ? 0 : Filters.Count);

                // Разбираем определение и генерим подзапрос
                List<String> queries = new List<string>();

                if (Filters != null)
                {
                    foreach (Filter filter in Filters)
                    {
                        if (filter.IsActive)
                        {
                            // Пересчитываем значения с учетом обстановки
                            filter.ResolveFilterValue(HttpContext.Current.Request, this.Page.IsPostBack, this.ViewState);

                            String q = filter.Query();

                            if (!String.IsNullOrEmpty(q))
                                queries.Add(q);

                            if (ShowDebug)
                                log("{0} -> {1}", filter.Info(), String.IsNullOrEmpty(q) ? SPUtility.GetLocalizedString("$Resources:LogEmptyQuery", "Examples.CAMLFilter", locale) : q);

                        }
                        else
                            if (ShowDebug) log("{0} -> <font color='red'>{1}</font>", filter.Info(), SPUtility.GetLocalizedString("$Resources:LogFilterInactive", "Examples.CAMLFilter", locale));


                    }
                }


                String query = Filter.JoinQueries(queries, UseOrConnections ? "Or" : "And");

                // Если что-то сгенерили - лезем в мякотку соседней вебчасти
                if (!String.IsNullOrEmpty(query))
                {
                    if (ShowDebug && queries.Count > 1) log(SPUtility.GetLocalizedString(this.UseOrConnections ? "$Resources:LogUseOr" : "$Resources:LogUseAnd", "Examples.CAMLFilter", locale));


                    if (ShowDebug || ShowInfo) log(SPUtility.GetLocalizedString("$Resources:LogFilterUsed", "Examples.CAMLFilter", locale));

                    // Встраиваем запрос в схему веб-части просмотра
                    XDocument doc = XDocument.Parse(listView.XmlDefinition);

                    if (ShowDebug) log(SPUtility.GetLocalizedString("$Resources:LogBaseSchema", "Examples.CAMLFilter", locale), doc.ToString());

                    XElement subQuery = XElement.Parse(query.ToString());

                    if (ShowDebug) log(SPUtility.GetLocalizedString("$Resources:LogQuery", "Examples.CAMLFilter", locale), subQuery.ToString());

                    // Берем корневой элемент View
                    XElement viewNode = doc.Descendants("View").FirstOrDefault();
                    // Если его нет - капец, уходим
                    if (viewNode == null)
                        throw new Exception(String.Format(SPUtility.GetLocalizedString("$Resources:ErrBadView", "Examples.CAMLFilter", locale), listView.DisplayName));

                    // Ищем элемент запроса
                    XElement queryNode = viewNode.Descendants("Query").FirstOrDefault();
                    // Если нет такого - создаем
                    if (queryNode == null)
                    {
                        queryNode = new XElement("Query");
                        viewNode.Add(queryNode);
                    }

                    // Внутри него ищем Where
                    XElement whereNode = queryNode.Descendants("Where").FirstOrDefault();
                    // Если нет - создаем
                    if (whereNode == null)
                    {
                        whereNode = new XElement("Where");
                        queryNode.Add(whereNode);
                    }

                    // Теперь смотрим, если Where не пустой
                    if (whereNode.Nodes().Count() == 0)
                    {
                        whereNode.Add(subQuery);
                    }
                    else
                    {
                        // Если внутри что-то есть - берем это и объединяем с нашим
                        XElement andNode = new XElement("And");
                        andNode.Add(whereNode.Nodes());
                        andNode.Add(subQuery);
                        // и заменяем старое содержимое Where
                        whereNode.RemoveAll();
                        whereNode.Add(andNode);
                    }

                    if (ShowDebug) log(SPUtility.GetLocalizedString("$Resources:LogModifiedSchema", "Examples.CAMLFilter", locale), doc.ToString());

                    listView.XmlDefinition = doc.ToString(SaveOptions.DisableFormatting);

                }
                else
                {
                    if (ShowDebug || ShowInfo) log(SPUtility.GetLocalizedString("$Resources:LogFilterNotUsed", "Examples.CAMLFilter", locale));
                }
            }
            catch (Exception ex)
            {
                pMain.Controls.Add(new LiteralControl(String.Format(SPUtility.GetLocalizedString("$Resources:ErrorPrompt", "Examples.CAMLFilter", locale), ex.Message) + "<br/>"));
            }
        }

        #endregion IWebPartTable implementation

        #region ICAMLFilter provider implementation
        [ConnectionProvider("CAML Filters", "CAMLFilterProvider")]
        public ICAMLFilter CAMLFilterProvider()
        {
            return null;
        }
        #endregion ICAMLFilter provider implementation
    }

    /// <summary>
    /// Интерфейс для связи с форомой поиска. Данные по нему передаваться не будут
    /// </summary>
    public interface ICAMLFilter
    {
    }

}
